package controller.solver;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.Timer;

import model.cnf.CNF;
import model.preprocessor.PreProcessorParameter;
import model.solver.IllegalSolverArgumentException;
import model.solver.SolverParameter;
import model.solver.SolverResult;

/**
 * Represent a solver.
 * Override checkParameter() to put some limitation on the parameter that can be use with your solver, by default it just call the {@link PreProcessorParameter} method to check parameter .
 * 
 * getCommandLine() return the list of parameter that will be use to run the solver
 * runSolver() is use to call the solver
 * hasTimeOut() is to know if your solver has some mechanism that allow it to stop itself after a certain time, if not this method should return false
 * 
 * evaluate() will evaluate the CNF using the method describe previously, it will get the parameter and run the solver. If the solver can't deal with the timeout, it will start a timer that will destroy the processus when the timeout is over.
 * 
 * Solver that can't deal with timeout must set the attribute "running" at true when it is running, and use the attribute "process" to store the process executing the preprocessor in order to allow the internal timeout mechanism to work.   
 * 
 * @author stephane
 *
 */
public abstract class Solver implements ActionListener{
	public static final int ADD_DELAY = 200; 
	
	protected String path = "";
	protected Timer timer = new Timer(PreProcessorParameter.DEFAULT_DELAY + ADD_DELAY, this);
	private boolean stop = false;
	private boolean running = false;

	
	/**
	 * store the process in this attribute
	 */
	protected Process process = null;
			
	/**
	 * if the solver is running
	 * @return
	 */
	public boolean isRunning() {
		return running;
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

	/**
	 * if the solver has been stop
	 * @return
	 */
	public boolean hasBeenStop() {
		return stop;
	}
	
	/**
	 * return the path of the file associate to the solver
	 * @return the path
	 */
	public String getPath() {
		return path;
	}
	
	/**
	 * determine if the solver has a timeout mechanism that allow him to stop after a certain time
	 * @return if it has one
	 */
	public abstract boolean hasTimeout();
	
	/**
	 * check if the parameter are good for this solver
	 * @return if they are allright
	 */
	public abstract boolean checkParameter();
	
	/**
	 * return the solver parameter
	 * @return the parameter
	 */
	public abstract SolverParameter getSolverParameter(); 
	
	/**
	 * return the arguments to give to the real solver 
	 * @param cnfFile the cnf 
	 * @return a array of String equivalent to the command line you would write on a terminal
	 */
	public abstract String[] getCommandLine(String cnfFile);
	
	/**
	 * run the preprocessor with the specified arguments
	 * @param cmd arguments
	 * @return the associate {@link SolverResult}
	 */
	public abstract SolverResult runSolver(String[] cmd);
	
	/**
	 * evaluate a CNF 
	 * @param cnf cnf to evaluate
	 * @param param parameter
	 * @return the result of the evaluation
	 * @throws IllegalSolverArgumentException 
	 */
	public SolverResult evaluate(CNF cnf) throws IllegalSolverArgumentException{
		if(cnf == null)
			throw new IllegalArgumentException();
		if(!checkParameter())
			throw new IllegalSolverArgumentException();
		
		stop = running = false;
		process = null;
		
		//recupere un fichier avec la cnf
		File tempIn = cnf.getCNFfile();//cnf.export(directory);
		if(tempIn == null)
			throw new IllegalArgumentException();
			
		String[] cmdArray = getCommandLine(tempIn.getPath());
		if(cmdArray == null)
			return null;
				
		if(getSolverParameter().needTimeOut() && !hasTimeout())
		{//gere le timeout manuellement
			timer = new Timer(getSolverParameter().getTimeOut() + ADD_DELAY, this);
			timer.start();
			System.out.println("demarre timer "+(getSolverParameter().getTimeOut() + ADD_DELAY));
		}

		SolverResult res = runSolver(cmdArray);
		
		//arrete timer
		timer.stop();
		//tempIn.delete();
		
		return res;
	}


	/**
	 * substitute the " " with "\ " for command argument in linux
	 * @param arg the original argument
	 * @return the argument with the "\ " instead of the " "
	 */
	public static String correctFormatForLinux(String arg){
		String res;
		if(arg.indexOf(' ') != -1)
			  res = arg.substring(0, arg.indexOf(' ')) + "\\ " + arg.substring(arg.indexOf(' ')+1);
		else res = arg;

		return res;
	}
	
	@Override
	public void actionPerformed(ActionEvent arg0) {
		
		if(arg0.getSource() == timer)
		{//doit terminer l'execution
			timer.stop();
			if(running && process != null)
			{//arrete 
				System.out.println("arrete le proc cause timeout");
				running =false;
				stop = true;
				process.destroy();
			}
		}
	}
	}
